using System;
using System.Collections.Generic;
using System.Text;
using Mpi.Common.Collections;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace Xeml.Document
{
    public class Sample : XemlAnnotatableObject,  INotifyPropertyChanged
    {
        private TimeIntermix _timeInterMix = TimeIntermix.Min;
        private int _id;
        List<Partition> _partitions;
        private bool _cachedTimeIsValid = false;
        private TimeSpan _cachedTime;


        #region Properties

        /// <summary>
        /// A property which indicates how the time point should be calculated.
        /// </summary>
        public TimeIntermix TimeIntermix
        {
            get
            {
                return this._timeInterMix;
            }
            set
            {
                if (value != _timeInterMix)
                {
                    this._timeInterMix = value;
                    _cachedTimeIsValid = false;
                    OnPropertyChanged("TimeIntermix");
                }
            }
        }

        /// <summary>
        /// The identifiere of the sample.
        /// </summary>
        public int Id
        {
            get
            {
                return this._id;
            }
            set
            {
                if (_id != value)
                {
                    this._id = value;
                    OnPropertyChanged("Id");
                }
            }
        }

        /// <summary>
        /// The collection of partitions on which the sample is build up.
        /// </summary>
        public ReadOnlyCollection<Partition> Partitions
        {
            get
            {
                return new ReadOnlyCollection<Partition>(_partitions);
            }
        }

        /// <summary>
        /// The calculated time point of an sample. The calculation is done in respect to the TimeIntermix property.
        /// </summary>
        public TimeSpan TimePoint
        {
            get
            {
                //if (_cachedTimeIsValid)
                //    return _cachedTime;
             //   List<TimeSpan> times = new List<TimeSpan>();
                
                TimeSpan ret = TimeSpan.Zero;
                switch (this.TimeIntermix)
                {
                    case TimeIntermix.Min:
                        {
                            if (_owningObPoints.Count > 0)
                                ret = _owningObPoints[0].TimePoint;
                            foreach (ObservationPoint ts in _owningObPoints)
                            {
                                ret = TimeSpan.FromTicks(Math.Min(ts.TimePoint.Ticks, ret.Ticks));
                            }
                            break;
                        }
                    case TimeIntermix.Max:
                        {
                            foreach (ObservationPoint ts in _owningObPoints)
                            {
                                ret = TimeSpan.FromTicks(Math.Max(ts.TimePoint.Ticks, ret.Ticks));
                            }
                            break;
                        }
                    case TimeIntermix.Average:
                        {
                            TimeSpan sum = TimeSpan.Zero;
                            foreach (ObservationPoint ts in _owningObPoints)
                            {
                                sum = sum + ts.TimePoint;
                            }
                            ret = TimeSpan.FromTicks(sum.Ticks / _owningObPoints.Count);
                            break;
                        }

                }
                //_cachedTime = ret;
                //_cachedTimeIsValid = true;
                return ret;
            }
        }
        #endregion

        #region ctor
        public Sample()
        {
            this._partitions = new List<Partition>();

        }

        #endregion

        List<ObservationPoint> _owningObPoints = new List<ObservationPoint>();
        private void CalcOwningObPoints()
        {
            _owningObPoints.Clear();
            foreach (Partition p in this._partitions)
            {
                Observation o = p.Collection.Observation;
                if (o != null)
                {

                    ObservationPoint op = o.Collection.ObservationPoint;
                    if (op != null && !_owningObPoints.Contains(op))
                    {
                        _owningObPoints.Add(op);
                    }
                }
            }
        }


        public void AddPartition(Partition part)
        {
            //_cachedTimeIsValid = false;
            _partitions.Add(part);
            CalcOwningObPoints();
            OnPropertyChanged("Partitions");
        }

        public void AddPartitionRange(IEnumerable<Partition> parts)
        {
            //_cachedTimeIsValid = false;
            _partitions.AddRange(parts);
            CalcOwningObPoints();
            OnPropertyChanged("Partitions");
        }

        public void RemovePartition(Partition part)
        {
           // _cachedTimeIsValid = false;
            _partitions.Remove(part);
            CalcOwningObPoints();
            OnPropertyChanged("Partitions");
        }

        public void ClearPartitions()
        {
            //_cachedTimeIsValid = false;
            _partitions.Clear();
            CalcOwningObPoints();
            OnPropertyChanged("Partitions");
        }

        public override int GetHashCode()
        {
            int hash = 0;
            StoryBase sb = this.Collection.Story;
            if (sb != null)
                hash ^= sb.StoryBoard.IndexOf(sb).GetHashCode();

            hash ^= TimePoint.GetHashCode();

            List<string> germplasmList = new List<string>();
            foreach (var item in Partitions)
            {
                Observation o = item.Collection.Observation;
                if (!germplasmList.Contains(o.IndividualsPool.FqName))
                    germplasmList.Add(o.IndividualsPool.FqName);
            }

            foreach (var item in germplasmList)
            {
                hash ^= item.GetHashCode();
            }

            
            foreach (var part in Partitions)
            {
                foreach (Term mat in part.Material)
                {
                    hash ^= mat.TermId.GetHashCode();
                }
            }


         

            return hash;

        }



        private SampleCollection _collection;
        public SampleCollection Collection
        {
            get
            {
                return this._collection;
            }
            set
            {
                if (value !=  _collection)
                {
                    this._collection = value;
                }

            }
        }


        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        #endregion
    }
}
